﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Web.Caching;
using System.Xml.Linq;

using ProjectBase.Core;

namespace $safeprojectname$.Xml
{
    public abstract class XmlDao : IDao
    {
        #region members
        public const string ConstInclude = "include";
        public const string ConstHref = "href";
        public static readonly XNamespace ConstNsInclude = "http://www.w3.org/2001/XInclude";
        #endregion members

        #region properties
        /// <summary>
        ///     The collection of the XDocuments stored in the IXmlRequestSession object.
        ///     When there is transaction, all of them are Saved on TrasactionCommit of the XmlSession object
        /// </summary>
        protected IDictionary<string, XDocument> XDocuments
        {
            get { return XmlSession.Current; }
        }

        protected void Flush()
        {
            XmlSession.TransactionCommit();
        }

        /// <summary>
        ///     The XDocument representing the xml file.
        ///     Object is maintained in the Cache until the underlying file is changed.
        /// </summary>
        protected virtual XDocument DataSource
        {
            get
            {
                var document = this.Cache()[DataSourcePath] as XDocument;
                if (document.IsNull())
                {
                    document = XDocument.Load(DataSourcePath);

                    if (DoSupportXInclude)
                    {
                        // XInclude
                        while (document.Descendants(ConstNsInclude + ConstInclude).Count() > 0)
                        {
                            var child = document.Descendants(ConstNsInclude + ConstInclude).First();
                            child.ReplaceWith(ReplaceInclude(child));
                        }
                    }
                    using (var cacheDependency = new CacheDependency(DataSourcePath))
                    {
                        this.Cache().Insert(DataSourcePath, document, cacheDependency);
                    }
                }
                return document;
            }
        }

        /// <summary>
        ///     default is false.
        ///     If set to true, al the XInclude elements are replaced
        ///     with their targets specified in href attribute.
        ///     Attention, when used, save operation is dangerous,
        ///     because only the root document is saved with replacements
        /// </summary>
        protected virtual bool DoSupportXInclude
        {
            get { return false; }
        }

        /// <summary>
        ///     Full path to the XDocument underlying file
        /// </summary>
        protected abstract string DataSourcePath { get; }

        /// <summary>
        ///     Extends the Linq to XML with XInclude syntax.
        ///     XML file can be split in more files.
        /// </summary>
        /// <param name = "include"></param>
        /// <returns></returns>
        protected virtual XElement ReplaceInclude(XElement include)
        {
            if (include.IsNull()
                || include.Attribute(ConstHref).IsNull())
            {
                return null; // this is not an include in fact
            }

            var path = include.Attribute(ConstHref).Value;
            if (!File.Exists(Global.CreateAbsolutePath(path)))
            {
                return null; // the hyperlink reference is not targeting existing file
            }

            XDocument doc; // tharget could be some binary, or damaged file
            doc = XDocument.Load(Global.CreateAbsolutePath(path));

            while (doc.Descendants(ConstNsInclude + ConstInclude).Count() > 0)
            {
                var child = doc.Descendants(ConstNsInclude + ConstInclude).First();
                child.ReplaceWith(ReplaceInclude(child)); // the same for child XInlcudes
            }

            return doc.Root; // return changed complete graph
        }
        #endregion properties
    }
}